package com.newcoder.service.impl;

import com.google.code.kaptcha.Producer;
import com.newcoder.entity.LoginTicket;
import com.newcoder.entity.User;
import com.newcoder.mapper.UserMapper;
import com.newcoder.service.UserService;
import com.newcoder.util.MailClient;
import com.newcoder.util.RedisKeyUtil;
import com.newcoder.util.ResponseWrapper;
import com.newcoder.util.UuidUtil;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static com.newcoder.util.CommunityConstant.DEFAULT_EXPIRED_SECONDS;
import static com.newcoder.util.CommunityConstant.REMEMBER_EXPIRED_SECONDS;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author yjh
 * @since 2024-12-04 10:03:26
 */
@Service
public class UserServiceImpl implements UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper  userMapper;

    @Autowired
    private MailClient mailClient;

    @Autowired
    private Producer kaptchaProducer;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${community.path.domain}")
    private String domain;

    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Value("${community.path.upload}")
    private String uploadPath;

    /**
     * 事务管理
     * isolation
     *  - Isolation.DEFAULT：使用底层数据库默认的隔离级别。
     *  - Isolation.READ_UNCOMMITTED：读取未提交数据：1，2，3，4，5
     *  - Isolation.READ_COMMITTED：读取已提交数据：3，4，5
     *  - Isolation.REPEATABLE_READ：可重复读：5
     *  - Isolation.SERIALIZABLE：串行化：null
     *
     * propagation
     *  - Propagation.REQUIRED: 支持当前事务(外部事务),如果不存在则创建新事务.
     *  - Propagation.REQUIRES_NEW: 创建一个新事务,并且暂停当前事务(外部事务).
     *  - Propagation.NESTED: 如果当前存在事务(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就会REQUIRED一样.(以上三个常用)
     *
     *  - Propagation.SUPPORTS
     * 	- Propagation.MANDATORY
     *  - Propagation.NOT_SUPPORTED
     *  - Propagation.NEVER
     *
     * */
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public ResponseWrapper insertUser(User user) {
        // 空值处理
        if (user == null) {
            throw new IllegalArgumentException("参数不能为空!");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            return ResponseWrapper.parameterError("用户名不能为空！");
        }
        if (StringUtils.isBlank(user.getPassword())) {
            return ResponseWrapper.parameterError("密码不能为空!");
        }
        if (StringUtils.isBlank(user.getEmail())) {
            return ResponseWrapper.parameterError("邮箱不能为空!");
        }

        // 验证账号
        User userSQL = userMapper.selectByName(user.getUsername());
        if (userSQL != null) {
            return ResponseWrapper.parameterError("该账号已存在!");
        }
        // 验证邮箱
        userSQL = userMapper.selectByEmail(user.getEmail());
        if (userSQL != null) {
            return ResponseWrapper.parameterError("该邮箱已被注册!");
        }

        // 注册用户
        user.setSalt(UuidUtil.generateUUID().substring(0, 5));
        user.setPassword(UuidUtil.md5(user.getPassword() + user.getSalt()));
        user.setType(0);
        user.setStatus(0);
        user.setActivationCode(UuidUtil.generateUUID());
        //设置默认头像
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
        user.setCreateTime(new Date());

        int i = userMapper.insertUser(user);
        if (i != 1) {
            return ResponseWrapper.parameterError("新增失败");
        }
        return ResponseWrapper.markCustom(true, 200,"新增成功！");
    }

    @Override
    public ResponseWrapper deleteUserById(Integer id) {
        if (id == null) {
            return ResponseWrapper.parameterError("id不能为空！");
        }
        int i = userMapper.deleteUserById(id);
        if (i != 1) {
            return ResponseWrapper.parameterError("删除失败！");
        }
        return ResponseWrapper.markCustom(true, 200,"删除成功！");
    }

    @Override
    public ResponseWrapper updateUserById(User user) {
        if (user == null) {
            return ResponseWrapper.parameterError("参数不能为空！");
        }
        int i = userMapper.updateUserById(user);
        if (i != 1) {
            return ResponseWrapper.parameterError("修改失败！");
        }
        return ResponseWrapper.markCustom(true, 200,"修改成功！");
    }

    @Override
    public ResponseWrapper updateHeader(Integer id, MultipartFile headerImage){
        if (headerImage == null || id == null) {
            return ResponseWrapper.parameterError("参数不能为空！");
        }

        String fileName = headerImage.getOriginalFilename();
        // 获取文件名中的最后一个点号的位置
        int dotIndex = fileName.lastIndexOf(".");
        // 检查文件名中是否有有效的点号，并且点号不是第一个字符
        if (dotIndex == -1 || dotIndex == 0) {
            return ResponseWrapper.parameterError("文件的格式不正确！");
        }
        // 提取文件扩展名（不包括点号）
        String suffix = fileName.substring(dotIndex).toLowerCase();
        // 验证文件扩展名是否在允许的列表中
        if (!Arrays.asList(".png", ".jpg", ".jpeg").contains(suffix)) {
            return ResponseWrapper.parameterError("不支持的文件格式！");
        }

        // 生成随机文件名
        fileName = UuidUtil.generateUUID() + suffix;
        // 确定文件存放的路径
        File dest = new File(uploadPath + "/" + fileName);
        try {
            // 存储文件
            headerImage.transferTo(dest);
        } catch (IOException e) {
            logger.error("上传文件失败: " + e.getMessage());
            return ResponseWrapper.markCustom(false, 500, "上传文件失败,服务器发生异常!");
        }

        // 更新当前用户的头像的路径(web访问路径)
        // http://localhost:8080/community/user/header/xxx.png
        User user = userMapper.selectById(id);
        if (user == null) {
            return ResponseWrapper.parameterError("用户不存在！");
        }
        String headerUrl = domain + contextPath + "/user/header/" + fileName;
        int i = userMapper.updateHeader(user.getId(), headerUrl);
        if (i != 1) {
            return ResponseWrapper.parameterError("修改图片路径失败！");
        }
        return ResponseWrapper.markCustom(true, 200,"修改头像成功！");
    }

    @Override
    public ResponseWrapper getUserById(Integer id) {
        if (id == null) {
            return ResponseWrapper.parameterError("id不能为空！");
        }
        User user = userMapper.selectById(id);
        if (user == null) {
            return ResponseWrapper.parameterError("user查找失败！");
        }
        ResponseWrapper wrapper = ResponseWrapper.markCustom(true, 200,"查找成功！");
        wrapper.getData().put("user", user);
        return wrapper;
    }

    @Override
    public ResponseWrapper getUserByName(String username) {
        if (username == null) {
            return ResponseWrapper.parameterError("username不能为空！");
        }
        User user = userMapper.selectByName(username);
        if (user == null) {
            return ResponseWrapper.parameterError("user查找失败！");
        }
        ResponseWrapper wrapper = ResponseWrapper.markCustom(true, 200,"查找成功！");
        wrapper.getData().put("user", user);
        return wrapper;
    }

    @Override
    public ResponseWrapper selectUserPage(Integer status, Integer type, Integer currentPage, Integer pageSize) {
        //当前页第一个数据下标offset
        Integer currentNum = (currentPage - 1) * pageSize;
        //所有数据的数量
        Integer total = userMapper.selectUserTotal(status, type);
        //当前页数据
        List<User> list = userMapper.selectUserPage(status, type, currentNum, pageSize);

        Integer pages = total%pageSize==0 ? total/pageSize : total/pageSize+1;
        ResponseWrapper wrapper = ResponseWrapper.markSuccess();
        wrapper.getData().put("userList",list);                     //当前页数据
        wrapper.getData().put("total", total);                  //总数据个数
        wrapper.getData().put("currentPage", currentPage);      //当前页
        wrapper.getData().put("currentNum", currentNum);        //当前页第一个数据下标
        wrapper.getData().put("pageSize", pageSize);            //页大小
        wrapper.getData().put("pages", pages);                  //总页数
        return wrapper;
    }

    ////LoginService
    @Override
    public ResponseWrapper login(boolean rememberme, User user) {
        int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
        // 空值处理
        if (StringUtils.isBlank(user.getUsername())) {
            return ResponseWrapper.parameterError("用户名不能为空！");
        }
        if (StringUtils.isBlank(user.getPassword())) {
            return ResponseWrapper.parameterError("密码不能为空!");
        }
        // 验证账号
        User userSQL = userMapper.selectByName(user.getUsername());
        if (userSQL == null) {
            return ResponseWrapper.parameterError("该账号不存在!");
        }
        // 验证状态
        if (userSQL.getStatus() == 0) {
            return ResponseWrapper.parameterError("该账号未激活!");
        }
        // 验证密码
        String password = UuidUtil.md5(user.getPassword() + userSQL.getSalt());
        if (!userSQL.getPassword().equals(password)) {
            return ResponseWrapper.parameterError("密码不正确!");
        }

        // 登录成功，生成登录凭证CustomizeInterceptor
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(UuidUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));

        //存入redis
        String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
        redisTemplate.opsForValue().set(redisKey, loginTicket);

        ResponseWrapper wrapper = ResponseWrapper.markSuccess();
        wrapper.getData().put("user", userSQL);
        wrapper.getData().put("ticket", loginTicket.getTicket());
        return wrapper;
    }

    @Override
    public LoginTicket findLoginTicket(String ticket) {
        // return loginTicketMapper.selectByTicket(ticket);
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        return (LoginTicket) redisTemplate.opsForValue().get(redisKey);
    }

    @Override
    public void kaptcha(HttpServletResponse response){
        // 生成验证码
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);

        // 将验证码存入cookie
        String kaptchaOwner = UuidUtil.generateUUID();
        Cookie cookie = new Cookie("kaptcha", text);
        cookie.setMaxAge(60);
        cookie.setPath(contextPath);
        response.addCookie(cookie);

        // 将验证码存入Redis
        // String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        // redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);

        // 将突图片输出给浏览器
        response.setContentType("image/png");
        try {
            OutputStream os = response.getOutputStream();
            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            logger.error("响应验证码失败:" + e.getMessage());
        }
    }

    @Override
    public ResponseWrapper logout(String ticket) {
        // 使登录凭证失效
        // loginTicketMapper.updateStatus(ticket, 1);
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
        loginTicket.setStatus(1);
        redisTemplate.opsForValue().set(redisKey, loginTicket);

        return ResponseWrapper.markCustom(true, 200,"成功退出登录！");
    }

    @Override
    public ResponseWrapper register(User user) {
        // 空值处理
        if (user == null) {
            throw new IllegalArgumentException("参数不能为空!");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            return ResponseWrapper.parameterError("用户名不能为空！");
        }
        if (StringUtils.isBlank(user.getPassword())) {
            return ResponseWrapper.parameterError("密码不能为空!");
        }
        if (StringUtils.isBlank(user.getEmail())) {
            return ResponseWrapper.parameterError("邮箱不能为空!");
        }

        // 验证账号
        User userSQL = userMapper.selectByName(user.getUsername());
        if (userSQL != null) {
            return ResponseWrapper.parameterError("该账号已存在!");
        }
        // 验证邮箱
        userSQL = userMapper.selectByEmail(user.getEmail());
        if (userSQL != null) {
            return ResponseWrapper.parameterError("该邮箱已被注册!");
        }

        // 注册用户
        user.setSalt(UuidUtil.generateUUID().substring(0, 5));
        user.setPassword(UuidUtil.md5(user.getPassword() + user.getSalt()));
        user.setType(0);
        user.setStatus(0);
        user.setActivationCode(UuidUtil.generateUUID());
        //设置默认头像
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
        user.setCreateTime(new Date());
        int i = userMapper.insertUser(user);
        if (i != 1) {
            return ResponseWrapper.parameterError("注册失败");
        }

        //激活邮件
        // http://localhost:8080/community/Login/activation/101/code
        String url = domain + contextPath + "/Login/activation?username=" + user.getUsername() + "&activationCode=" + user.getActivationCode();
        //发送邮件
        boolean b = mailClient.sendMail(user.getEmail(), "激活账号", "激活链接：" + url);
        if (!b) {
            return ResponseWrapper.parameterError("邮件发送失败");
        }

        return ResponseWrapper.markCustom(true, 200,"注册成功！");
    }

    @Override
    public ResponseWrapper activation(String username, String activationCode) {
        User user = userMapper.selectByName(username);
        if(user == null) {
            return ResponseWrapper.markCustom(false, 400,"用户不存在！");
        }
        if (user.getStatus() == 1) {
            return ResponseWrapper.markCustom(true, 200,"账号已经激活，无需重复激活！");
        } else if (user.getActivationCode().equals(activationCode)) {
            userMapper.updateStatus(user.getId(), 1);
            clearCache(user.getId());
            return ResponseWrapper.markCustom(true, 200,"账号激活成功！");
        } else {
            return ResponseWrapper.markCustom(false, 400,"账号激活失败！");
        }
    }

    // 1.优先从缓存中取值
    private User getCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(redisKey);
    }

    // 2.取不到时初始化缓存数据
    private User initCache(int userId) {
        User user = userMapper.selectById(userId);
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
        return user;
    }

    // 3.数据变更时清除缓存数据
    private void clearCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(redisKey);
    }
}
